home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / human interface toolbox / modeless dialog sample / dialogcode.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  16.2 KB  |  547 lines

  1. /*
  2.     File:        DialogCode.c
  3.  
  4.     Contains:    a sample application showing how to implement a 
  5.                 modeless dialog with a couple of controls.
  6.  
  7.     Written by: Nitin Ganatra    
  8.  
  9.     Copyright:    Copyright © 1994-1999 by Apple Computer, Inc., All Rights Reserved.
  10.  
  11.                 You may incorporate this Apple sample source code into your program(s) without
  12.                 restriction. This Apple sample source code has been provided "AS IS" and the
  13.                 responsibility for its operation is yours. You are not permitted to redistribute
  14.                 this Apple sample source code as "Apple sample source code" after having made
  15.                 changes. If you're going to re-distribute the source, we require that you make
  16.                 it clear in the source that the code was descended from Apple sample source
  17.                 code, but that you've made changes.
  18.  
  19.     Change History (most recent first):
  20.                 8/9/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  21.                 
  22.  
  23. */
  24.  
  25. #include <Dialogs.h>
  26. #include "SampleHeader.h"
  27.  
  28. extern Boolean gDone;
  29. extern Boolean gInBackground;
  30.  
  31. /*-------------------------------------------------------------------------------------*/
  32. /*
  33.     GetCtlHandle - Just a handy way to get a control's handle given only the item
  34.     number and the dialog.
  35. */
  36. static ControlHandle GetCtlHandle(DialogPtr theDialog, short theItem)
  37. {
  38.     Handle     theControl;
  39.     short    aType;
  40.     Rect    aRect;
  41.     
  42.     GetDialogItem(theDialog, theItem, &aType, &theControl, &aRect);
  43.     if (theControl == nil )
  44.         DebugStr((StringPtr)"\pGetDItem in GetCtlHandle failed");
  45.  
  46.     return (ControlHandle)theControl;
  47. }
  48.  
  49.  
  50. /*-------------------------------------------------------------------------------------*/
  51. /*
  52.     SetDialogControlHilite - makes the control active and sets the control value to
  53.     whatever you want.
  54.     
  55. */
  56. static void SetDialogControlHilite(DialogPtr theDialog, short whichItem, Boolean isHilited)
  57. {
  58.     ControlHandle    theControl;
  59.  
  60.     theControl = GetCtlHandle(theDialog, whichItem);
  61.     if (theControl == nil )
  62.     {
  63.         DebugStr((StringPtr)"\pGetCtlHandle in SetDialogControlHilite failed");
  64.         return;
  65.     }
  66.     HiliteControl(theControl, 0);
  67.     SetControlValue(theControl, isHilited);
  68. }
  69.  
  70.  
  71. /*-------------------------------------------------------------------------------------*/
  72. /*
  73.     DisableDialogControl - disables a dialog control by deactivating it (to turn off 
  74.     hit detection) and sets its value to 0.
  75. */
  76. static void DisableDialogControl(DialogPtr theDialog, short whichItem)
  77. {
  78.     ControlHandle    theControl;
  79.  
  80.     theControl = GetCtlHandle(theDialog, whichItem);
  81.     if ( theControl != nil )
  82.     {
  83.         SetControlValue(theControl, 0);
  84.         HiliteControl(theControl, 255);
  85.     }
  86.     else
  87.         DebugStr((StringPtr)"\pthe handle was nil");
  88. }
  89.  
  90.  
  91. /*-------------------------------------------------------------------------------------*/
  92. /*
  93.     DoDialogEvent - After checking that EventFilter's result is TRUE, this calls 
  94.     DialogSelect to check if any controls were hit, and if so this acts accordingly.
  95. */
  96. void DoDialogEvent(EventRecord *theEvent)
  97. {
  98.     DialogPtr    theDialog = (DialogPtr)FrontWindow();
  99.     short        theItem;
  100.     GrafPtr        origPort;
  101.  
  102.     GetPort(&origPort);
  103.     SetPort(theDialog);
  104.  
  105.     if ( EventFilter(theEvent, theDialog) != false )
  106.     {
  107.         if ( DialogSelect(theEvent, &theDialog, &theItem) == true )
  108.         {
  109.             if ( theDialog != nil )
  110.             {
  111.                 ControlHandle    tempHandle;
  112.                 short            newTEItem = kEditTextField;
  113.                 Boolean            newControlVal = true;
  114.                 
  115.                 switch ( theItem )
  116.                 {
  117.                     case kEditCheckItem:
  118.                         /* if this is hit, toggle the check box.  We'll also     */
  119.                         /* update the edit text field no matter what, since     */
  120.                         /* its state depends on the check item.                 */
  121.                         tempHandle = GetCtlHandle(theDialog, kEditCheckItem);
  122.  
  123.                         if ( GetControlValue(tempHandle) == true )
  124.                         {
  125.                             newControlVal = false;
  126.                             newTEItem = -1;
  127.                         } 
  128.     
  129.                         SetDialogControlHilite(theDialog, kEditCheckItem, newControlVal);
  130.                         SelectDialogItemText(theDialog, newTEItem, 0, 0);
  131.                         InvalThisItemRect(theDialog, kEditTextField);
  132.                         break;
  133.         
  134.                     case kEditTextField:
  135.                         break;
  136.                     
  137.                     case kCancelButton:
  138.                     case kOkayButton:
  139.                         /* in either case, just throw out the dialog and return    */
  140.                         DisposeDialog(theDialog);
  141.                         break;
  142.             
  143.                 }
  144.             }
  145.         }
  146.     }
  147.  
  148.     SetPort(origPort);
  149.     return;
  150.  
  151. BailOut:
  152.     DebugStr((StringPtr)"\pGetCtlHandle in DoDialogEvent failed");
  153. }
  154.  
  155.  
  156. /*-------------------------------------------------------------------------------------*/
  157. /*
  158.     FakeButtonHilite - The recommended way to fake a button being hit.  If the user 
  159.     hits escape to quit a dialog, this is called to give visual feedback that the
  160.     dialog is cancelled.
  161. */
  162. static void FakeButtonHilite(DialogPtr theDialog, short buttonItemNum)
  163. {
  164.     unsigned long throwAway;
  165.  
  166.     ControlHandle buttonHandle = GetCtlHandle(theDialog, buttonItemNum);
  167.     if ( buttonHandle != nil )
  168.     {
  169.         HiliteControl(buttonHandle, true);
  170.         Delay(8, &throwAway);
  171.         HiliteControl(buttonHandle, false);
  172.     }
  173.  
  174. }
  175.  
  176.  
  177. /*-------------------------------------------------------------------------------------*/
  178. /*
  179.     InvalThisItemRect - A handly way to invalidate a dialog item.  This is used to 
  180.     invalidate the edit text field of the dialog in response to the check box being 
  181.     hit.
  182. */
  183. static void InvalThisItemRect(DialogPtr theDialog, short theEditItem)
  184. {
  185.     Handle            editTextItem;
  186.     short            aType;
  187.     Rect            editTextRect;
  188.     GrafPtr            oldPort;
  189.  
  190.     GetPort(&oldPort);
  191.     SetPort(theDialog);
  192.     
  193.     GetDialogItem(theDialog, theEditItem, &aType, &editTextItem, &editTextRect);
  194.     InsetRect(&editTextRect, -5, -5);
  195.     InvalRect(&editTextRect);
  196.     
  197.     SetPort(oldPort);
  198. }
  199.  
  200.  
  201. /*-------------------------------------------------------------------------------------*/
  202. /*
  203.     CreateModelessDialog - Creates the modeless dialog (from a DLOG resource) and sets
  204.     up the initial state of the controls and user items.  Note that it also allocates
  205.     two globals that will always point to the user item proc routines.  This is needed
  206.     only once and can be called by all sebsequent calls to CreateModelessDialog, because
  207.     the routines won't change.  In the 68k world, these routines are redefined to just 
  208.     be plain old proc pointers, and on PowerPC bulids they use real UPPs.
  209. */
  210. void CreateModelessDialog(void)
  211. {
  212.     short     tempType;
  213.     Handle    tempHandle;
  214.     Rect    tempRect;
  215.     DialogPtr    theNewDialog;
  216.     static UserItemUPP    procForDimUserItem = nil;
  217.     static UserItemUPP    procForBorderUserItem = nil;
  218.  
  219.     theNewDialog = GetNewDialog(kModelessDialogID, nil, (WindowPtr)-1);
  220.  
  221.     if ( theNewDialog != nil )
  222.     {    
  223.         if ( procForDimUserItem == nil )
  224.             procForDimUserItem = NewUserItemProc(DimEditLine);    
  225.  
  226.         if ( procForBorderUserItem == nil )
  227.             procForBorderUserItem = NewUserItemProc(DrawButtonBorder);    
  228.     
  229.         SetDialogControlHilite(theNewDialog, kEditCheckItem, true);
  230.         
  231.         SelectDialogItemText(theNewDialog, kEditTextField, 0, 0);
  232.         
  233.         /* set up the user item that will dim the edit line when it is disabled */
  234.         GetDialogItem(theNewDialog, kEditUserItem, &tempType, &tempHandle, &tempRect);
  235.         SetDialogItem(theNewDialog, kEditUserItem, tempType, (Handle)procForDimUserItem, &tempRect);
  236.     
  237.         /* set up the user item that will draw the button border */
  238.         GetDialogItem(theNewDialog, kOkayUserItem, &tempType, &tempHandle, &tempRect);
  239.         SetDialogItem(theNewDialog, kOkayUserItem, tempType, (Handle)procForBorderUserItem, &tempRect);
  240.     }
  241.     else
  242.         DebugStr((StringPtr)"\pGetNewDialog failed");
  243. }
  244.  
  245.  
  246. /*-------------------------------------------------------------------------------------*/
  247. /*
  248.     DimEditLine - If the dialog is not frontmost and the edit check box is false, this
  249.     will draw the edit text dialog item in 'disabled' mode.
  250. */
  251. static pascal void DimEditLine(WindowPtr theDialog, short theItem)
  252. {
  253.     ControlHandle theControl;
  254.     
  255.     theControl = GetCtlHandle(theDialog, kEditCheckItem);
  256.     /* only do it if the checkbox is false */
  257.     if ( (GetControlValue(theControl) == false) || (theDialog != FrontWindow()) )
  258.     {
  259.         PenState     thePen;
  260.         short         itemType;
  261.         Handle         itemHandle;
  262.         Rect         dimRect;
  263.  
  264.         /* Save and restore the pen state so we don't mess things up for other */
  265.         /* drawing routines */
  266.         GetPenState(&thePen);
  267.         GetDialogItem(theDialog, theItem, &itemType, &itemHandle, &dimRect);
  268.         PenMode(notPatBic);
  269.         PenPat(&qd.gray);
  270.         PaintRect(&dimRect);
  271.         SetPenState(&thePen);
  272.         
  273.     }
  274. }
  275.  
  276.  
  277. /*-------------------------------------------------------------------------------------*/
  278. /*
  279.     HiliteAllControls - This is used in response to activate events for the dialog.  If
  280.     an activate event is sent, hilite all controls to be active, and if its a deactivate
  281.     event, unhilite all controls.
  282. */
  283. static void HiliteAllControls(DialogPtr theDialog, short hiliteCode)
  284. {
  285.     ControlHandle theControl;
  286.  
  287.     InvalThisItemRect(theDialog, kEditTextField);
  288.  
  289.     theControl = GetCtlHandle(theDialog, kEditCheckItem);
  290.     if ( theControl != nil )
  291.         HiliteControl(theControl, hiliteCode);
  292.  
  293.     theControl = GetCtlHandle(theDialog, kCancelButton);
  294.     if ( theControl != nil )
  295.         HiliteControl(theControl, hiliteCode);
  296.  
  297.  
  298.     theControl = GetCtlHandle(theDialog, kOkayButton);
  299.     if ( theControl != nil )
  300.         HiliteControl(theControl, hiliteCode);
  301.  
  302. }
  303.  
  304.  
  305. /*-------------------------------------------------------------------------------------*/
  306. /*
  307.     DrawButtonBorder - Standard way to draw a border around the default button.
  308. */
  309. static pascal void DrawButtonBorder(WindowPtr theDialog, short theItem)
  310. {
  311. #pragma unused (theItem)
  312.     Handle            theControl;
  313.     short            aType;
  314.     Rect            aRect;
  315.  
  316.     GetDialogItem((DialogPtr)theDialog, kOkayButton, &aType, &theControl, &aRect);
  317.     
  318.     PenSize(3,3);
  319.     InsetRect(&aRect, -4, -4);
  320.     FrameRoundRect(&aRect, 16,16);
  321. }
  322.  
  323.  
  324. /*-------------------------------------------------------------------------------------*/
  325. /*
  326.     DoCut - Zero out the scrap and cut the current selection from the current edit text
  327.     field.  Done in response to the Cut menu command (either by key or mouse).
  328.  
  329.     Note that we don't have to worry about putting the TEScrap->DeskScrap stuff
  330.     in response to suspend/resume events, because the desk scrap is always
  331.     kept up-to-date.
  332. */
  333. void DoCut(DialogPtr theDialog)
  334. {
  335.     HiliteMenu(kEditMenu);
  336.     (void)ZeroScrap();
  337.     DialogCut(theDialog);
  338.     if ( TEToScrap() != noErr )
  339.         DebugStr((StringPtr)"\pProblem going from TE->desk scrap");
  340. }
  341.  
  342.  
  343. /*-------------------------------------------------------------------------------------*/
  344. /*
  345.     DoCut - Zero out the scrap and copy the current selection from the current edit text
  346.     field.  Done in response to the Copy menu command (either by key or mouse).
  347.  
  348.     Note that we don't have to worry about putting the TEScrap->DeskScrap stuff
  349.     in response to suspend/resume events, because the desk scrap is always
  350.     kept up-to-date.
  351. */
  352. void DoCopy(DialogPtr theDialog)
  353. {
  354.     HiliteMenu(kEditMenu);
  355.     (void)ZeroScrap();
  356.     DialogCopy(theDialog);
  357.     if ( TEToScrap() != noErr )
  358.         DebugStr((StringPtr)"\pProblem going from TE->desk scrap");
  359. }
  360.  
  361.  
  362. /*-------------------------------------------------------------------------------------*/
  363. /*
  364.     DoPaste - Paste the current selection from the current edit text
  365.     field.  Done in response to the Paste menu command (either by key or mouse).
  366.  
  367.     Note that we don't have to worry about putting the TEScrap->DeskScrap stuff
  368.     in response to suspend/resume events, because the desk scrap is always
  369.     kept up-to-date.
  370. */
  371. void DoPaste(DialogPtr theDialog)
  372. {
  373.     if ( TEFromScrap() != noErr )
  374.         DebugStr((StringPtr)"\pProblem going from desk scrap->TE");
  375.     HiliteMenu(kEditMenu);
  376.     DialogPaste(theDialog);
  377.  
  378. }
  379.  
  380.  
  381. /*-------------------------------------------------------------------------------------*/
  382. /*
  383.     DoClear - Cut the current selection from the current edit text field, but don't
  384.     add it to the scrap.  Done in response to the Cut menu command (either by key 
  385.     or mouse).
  386. */
  387. void DoClear(DialogPtr theDialog)
  388. {
  389.     HiliteMenu(kEditMenu);
  390.     DialogDelete(theDialog);
  391. }
  392.  
  393.  
  394. /*-------------------------------------------------------------------------------------*/
  395. /*
  396.     DoDialogNullEvent - This handles updating the cursor as it flys over an active edit
  397.     text field.  It also calls DialogSelect which is necessary if you have any edit
  398.     text dialog fields, and you want that cursor to blink.
  399. */
  400. void DoDialogNullEvent(EventRecord *theEvent)
  401. {
  402.     WindowPtr    currFrontWindow = FrontWindow();
  403.     short        throwAwayItem;
  404.     GrafPtr        origPort;
  405.     
  406.     GetPort(&origPort);
  407.     SetPort(currFrontWindow);
  408.     
  409.     if ( currFrontWindow != nil )
  410.     {
  411.         if ( (((WindowRecord *)currFrontWindow)->windowKind == dialogKind)
  412.             && !gInBackground )
  413.         {
  414.             Point    tempPt;
  415.             Rect    editFieldRect;
  416.             short    throwAwayType;
  417.             Handle    throwAwayHandle;
  418.             
  419.             GetDialogItem(currFrontWindow, kEditTextField, &throwAwayType, &throwAwayHandle, &editFieldRect);
  420.             GetMouse(&tempPt);
  421.             if ( PtInRect(tempPt, &editFieldRect) 
  422.                 && (GetControlValue(GetCtlHandle(currFrontWindow, kEditCheckItem)) == true) )
  423.             {
  424.                 CursHandle    theCursor;
  425.         
  426.                 theCursor = GetCursor(iBeamCursor);
  427.                 HLock((Handle)theCursor);
  428.                 SetCursor(*theCursor);
  429.                 HUnlock((Handle)theCursor);
  430.             }
  431.             else
  432.                 SetCursor(&qd.arrow);
  433.                 
  434.             DialogSelect(theEvent, &currFrontWindow, &throwAwayItem);
  435.  
  436.         }
  437.     }
  438.  
  439.     SetPort(origPort);
  440.  
  441. }
  442.  
  443.  
  444. /*-------------------------------------------------------------------------------------*/
  445. /*
  446.     EventFilter - This is the first thing done if a dialog event is received, giving 
  447.     you a chance to perform any special stuff before passing control on to 
  448.     DialogSelect.
  449.     
  450.     If this routine returns true, the event processing will continue, and DialogSelect
  451.     will be called to perform hit detection on the controls.  If false is returned,
  452.     it means the event is already handled and the main event loop will continue.
  453. */
  454. Boolean EventFilter(EventRecord *theEvent, WindowPtr theFrontWindow)
  455. {
  456.     Handle        editFieldHandle;
  457.     short        aType;
  458.     Point        tempPt;
  459.     Rect        editFieldRect;
  460.     char         theKey;
  461.     short         theHiliteCode;
  462.     
  463.     if ( ((WindowRecord *)theFrontWindow)->windowKind == dialogKind )
  464.         GetDialogItem((DialogPtr)theFrontWindow, kEditTextField, &aType, &editFieldHandle, &editFieldRect);
  465.  
  466.     switch ( theEvent->what )
  467.     {    
  468.         case mouseDown:
  469.         case mouseUp:
  470.                 tempPt = theEvent->where;
  471.                 GlobalToLocal(&tempPt);
  472.  
  473.                 /* Check if the mouseDown was in the editText field         */
  474.                 /* (like for text selection or cursor placement.  If         */
  475.                 /* the checkbox isn't checked, do nothing and return         */
  476.                 /* false so DialogSelect doesn't mess with it.                 */
  477.                 if ( PtInRect(tempPt, &editFieldRect) && 
  478.                     (GetControlValue(GetCtlHandle((DialogPtr)theFrontWindow, kEditCheckItem)) == false) )
  479.                     return false;
  480.                 break;
  481.  
  482.         case keyDown:
  483.         case autoKey:    
  484.                 theKey = theEvent->message & charCodeMask;
  485.  
  486.                 if ( (theEvent->modifiers & cmdKey) != 0 ) 
  487.                 {    
  488.                     long menuResult;
  489.                 
  490.                     AdjustMenus();
  491.                     menuResult = MenuKey(theKey);
  492.             
  493.                     if ( (menuResult  >> 16) != 0 )
  494.                     {
  495.                         Boolean editOpPerformed = MenuCommand(menuResult);
  496.                         
  497.                         if ( editOpPerformed == true )
  498.                             /* You may ask yourself, "Why are we exiting when an     */
  499.                             /* edit operation is performed?"  Well, DialogSelect     */
  500.                             /* performs some automatic handling for any editText     */
  501.                             /* items that may be in the Dialog, and since we've     */
  502.                             /* already handled them in MenuCommand() we don't         */
  503.                             /* want  DialogSelect to do anything                     */
  504.                             return false;
  505.                     }
  506.                 }
  507.                 else if ( ((theEvent->message) & charCodeMask) == 0x1B )
  508.                 {
  509.                     /* Was Cancel hit?  We could also check for Return     */
  510.                     /* or Enter being hit, but in this example the edit */
  511.                     /* text field is multi-lined, so I'm quitting on     */
  512.                     /* return.  You get the idea..                         */
  513.                     FakeButtonHilite((DialogPtr)theFrontWindow, kCancelButton);
  514.                     DisposeDialog((DialogPtr)theFrontWindow);
  515.                     return false;
  516.                 }
  517.                 
  518.                 if ( (((DialogPeek)theFrontWindow)->editField + 1 == kEditTextField) 
  519.                     && (GetControlValue(GetCtlHandle((DialogPtr)theFrontWindow, kEditCheckItem)) == false) )
  520.                     /* Finally, if any non-command keystrokes were         */
  521.                     /* entered and the edit field is disabled, exit.     */
  522.                     return false;
  523.  
  524.                 break;
  525.  
  526.         case activateEvt:                
  527.                 /* This is where we take care of hiliting the        */
  528.                 /* controls according to whether or not the dialog     */
  529.                 /* is frontmost.                                     */
  530.                 theFrontWindow = (WindowPtr)theEvent->message;
  531.                 if ( (theEvent->modifiers & activeFlag) == true )
  532.                     theHiliteCode = 0;
  533.                 else
  534.                     theHiliteCode = 255;
  535.  
  536.                 HiliteAllControls((WindowPtr)theFrontWindow, theHiliteCode);
  537.                 return false;
  538.  
  539.     }
  540.  
  541.     /* If we haven't returned false by now, go ahead    */
  542.     /* and return true so DialogSelect can do its         */
  543.     /* thing, like update the window, deal with an         */
  544.     /* item hit, etc.                                    */
  545.     return true;
  546. }
  547.